package cn.chinaunicom.sdsi.frm.workflow.component.impl;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import cn.chinaunicom.sdsi.frm.workflow.bean.BaseTaskVO;
import cn.chinaunicom.sdsi.frm.workflow.bean.HistoicFlowVO;
import cn.chinaunicom.sdsi.frm.workflow.bean.ProcInstInfo;
import cn.chinaunicom.sdsi.frm.workflow.bean.WorkflowEntityForStart;
import cn.chinaunicom.sdsi.frm.workflow.cmd.JumpToActivityCmd;
import cn.chinaunicom.sdsi.frm.workflow.common.WorkflowConstants;
import cn.chinaunicom.sdsi.frm.workflow.component.WorkflowManager;
import cn.chinaunicom.sdsi.frm.workflow.ext.StaffBaseInfo;
import cn.chinaunicom.sdsi.frm.workflow.task.dao.FlowHistoryDao;

/**
 * 工作流封装功能业务组件
 * 
 * @author liyh
 */
@Component
public class WorkflowManagerImpl implements WorkflowManager {

	private static final Log log = LogFactory.getLog(WorkflowManagerImpl.class);

	/**
	 * 工作流引擎工厂Bean
	 */
	private ProcessEngineFactoryBean processEngine;
	/**
	 * 流程文件部署及流程定义业务组件
	 */
	private RepositoryService repositoryService;
	/**
	 * 流程运行时控制组件
	 */
	private RuntimeService runtimeService;
	/**
	 * 任务服务组件
	 */
	private TaskService taskService;
	/**
	 * 历史数据查询及删除服务组件
	 */
	private HistoryService historyService;
	/**
	 * 流程引擎相关信息服务组件
	 */
	private ManagementService managementService;
	/**
	 * 流程引擎中人员管理组件，仅用于发起时保存发起人，尽量避免使用
	 */
	private IdentityService identityService;

	/**
	 * 流转历史组件
	 */
	private FlowHistoryDao flowHistoryDao;

	/**
	 * 业务系统实现的人员信息抽取接口
	 */
	private StaffBaseInfo staffBaseInfo;

	/**
	 * 原生API将流程定义编码转义至定义唯一编码
	 * 
	 * @param processDefinitionKey
	 *            流程定义key
	 * @return 流程定义唯一编码
	 */
	@Override
	public String convertToDefinitionId(String processDefinitionKey) {

		List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
				.processDefinitionKey(processDefinitionKey).orderByProcessDefinitionVersion().desc().list();
		String processDefinitionId = list.get(0).getId();

		return processDefinitionId;
	}

	/**
	 * 据流程实例ID获取实例相关信息
	 * 
	 * @param processInstanceId
	 *            流程实例ID
	 * @return 流程实例自定义信息实体
	 */
	@Override
	public ProcInstInfo getProcInstInfo(String processInstanceId) {

		// 实例在活动中，获取实例
		ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId)
				.singleResult();
		// 活动已完结，获取实例
		HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery()
				.processInstanceId(processInstanceId).singleResult();
		if (pi == null && hpi == null) {
			return null;
		}
		// 主业务数据主键
		String businessKey = pi == null ? hpi.getBusinessKey() : pi.getBusinessKey();
		// 流程定义相关信息
		String processDefinitionId = pi == null ? hpi.getProcessDefinitionId() : pi.getProcessDefinitionId();
		String processDefinitionKey = pi == null ? hpi.getProcessDefinitionKey() : pi.getProcessDefinitionKey();
		String processDefinitionName = pi == null ? hpi.getProcessDefinitionName() : pi.getProcessDefinitionName();

		ProcInstInfo pii = new ProcInstInfo(processInstanceId, businessKey, processDefinitionId, processDefinitionKey,
				processDefinitionName);

		return pii;
	}

	/**
	 * 据任务ID获取流程相关基本信息
	 * 
	 * @param taskId
	 *            任务ID
	 * @return 任务基本信息实体
	 */
	@Override
	public BaseTaskVO getBaseTaskVO(String taskId) {

		// 任务在等待中，获取任务
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		// 任务已完成，获取任务
		HistoricTaskInstance hTask = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
		if (task == null && hTask == null) {
			return null;
		}

		String name = task == null ? hTask.getName() : task.getName();
		String processInstanceId = task == null ? hTask.getProcessInstanceId() : task.getProcessInstanceId();
		String taskDefinitionKey = task == null ? hTask.getTaskDefinitionKey() : task.getTaskDefinitionKey();
		String processDefinitionId = task == null ? hTask.getProcessDefinitionId() : task.getProcessDefinitionId();
		String executionId = task == null ? hTask.getExecutionId() : task.getExecutionId();
		ProcInstInfo pii = this.getProcInstInfo(processInstanceId);

		BaseTaskVO bto = new BaseTaskVO(taskId, name, taskDefinitionKey, processInstanceId, processDefinitionId,
				pii.getProcessDefinitionKey(), executionId);

		return bto;
	}

	/**
	 * 流程发起公共方法说明： 1.参数类型WorkflowEntityForStart，每一项均必须填写，流程编码、流程标题、流程发起人等
	 * 2.vars中可以放入流程全局共享的参数 3.必须在vars中传入下环节处理人assignee
	 * 
	 * @param ii
	 *            流程发起时的必传参数
	 * @param vars
	 *            用户流程发起时需要的额外参数
	 * @param comment
	 *            首环节发起意见
	 * @param withdraw
	 *            推进后是否可撤回
	 * @return 流程实例ID
	 */
	@Override
	public String startProcess(WorkflowEntityForStart ii, Map<String, Object> vars, String comment, boolean withdraw) {

		/*
		 * 取出流程发起时的必要信息
		 * 
		 * 流程标题、流程编码、业务主键及发起人相关信息
		 */
		// 用户ID，多岗系统为当前岗位ID
		String userId = ii.getUserId();
		String userName = staffBaseInfo.getStaffName(userId);
		String orgId = staffBaseInfo.getStaffOrgId(userId);
		String orgName = staffBaseInfo.getStaffOrgName(userId);

		String title = ii.getTitle();
		String processDefinitionKey = ii.getProcessDefinitionKey();
		String businessKey = ii.getBusinessKey();

		// 暂存次环节处理人并设置手环节处理人为发起人
		String assignee = String.valueOf(vars.get("assignee"));
		vars.put("assignee", userId);

		// 存入流程标题
		vars.put("title", title);
		// 存储发起人名称
		vars.put("applyUserName", userName);
		// 将流程发起时信息以序列化形式存入流程的全局参数中
		// vars.put("instanceInfo", ii);

		/*
		 * 设置启动流程的人员ID，需要在开始事件中设置activiti:initiator=""
		 * 由于web容器使用线程池，而setAuthenticatedUserId后相当于设置了ThreadLocal变量，故需要清空
		 */
		identityService.setAuthenticatedUserId(userId);
		ProcessInstance pi = null;
		if (StringUtils.isBlank(businessKey)) {
			pi = runtimeService.startProcessInstanceByKey(processDefinitionKey, vars);
		} else {
			pi = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, vars);
		}
		identityService.setAuthenticatedUserId(null);
		String processInstanceId = pi.getId();
		log.debug("process has started , the processInstanceId is :" + processInstanceId);

		/*
		 * 默许规则，用户申请环节设置为流程开始时间后的第一个环节，默认自动完成首环节 拾取用户发起流程时传入的意见作为首环节的处理意见
		 */
		Task startTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
		HistoicFlowVO firstHistoicFlow = new HistoicFlowVO();
		firstHistoicFlow.setProcessInstanceId(processInstanceId);
		firstHistoicFlow.setTaskId(startTask.getId());
		firstHistoicFlow.setPreTaskId("");
		firstHistoicFlow.setLinkKey(startTask.getTaskDefinitionKey());
		firstHistoicFlow.setLinkName(startTask.getName());
		firstHistoicFlow.setAssigneeId(userId);
		firstHistoicFlow.setAssigneeName(userName);
		firstHistoicFlow.setOrgId(orgId);
		firstHistoicFlow.setOrgName(orgName);
		firstHistoicFlow.setHandleType(WorkflowConstants.HANDLE_TYPE_PASS);
		firstHistoicFlow.setStartTime(startTask.getCreateTime());
		this.addComment(comment, startTask.getId(), pi.getId(), true);
		firstHistoicFlow.setUserComment(comment);

		// 流程发起完成首环节任务当前人
		taskService.setAssignee(startTask.getId(), userId);
		// this.getTaskService().claim(task.getId(), userId);
		// 受理变更为用户传入值
		vars.put("assignee", assignee);
		taskService.complete(startTask.getId(), vars);
		firstHistoicFlow.setEndTime(new Date());
		firstHistoicFlow.setWithdraw(withdraw);
		firstHistoicFlow.setQuickBack(false);

		flowHistoryDao.insertWholeFlowHistory(firstHistoicFlow);
		log.debug("the first task has been done .");

		/**
		 * 完成流程推动后，如果存在新产生的用户任务，需要添加新增的历史记录，暂默认流程推进前的瞬间之后生成的任务均为待处理的用户任务
		 * 20170511修改，为兼容老版本数据库时间精度丢失问题
		 */
		List<Task> newTaskList = findNewTaskByInstanceId(processInstanceId);

		if (newTaskList != null && newTaskList.size() > 0) {

			for (int i = 0; i < newTaskList.size(); i++) {

				Task newTask = newTaskList.get(i);
				String newUserId = newTask.getAssignee();
				String newTaskId = newTask.getId();

				HistoicFlowVO nextHistoicFlow = new HistoicFlowVO();
				nextHistoicFlow.setProcessInstanceId(processInstanceId);
				nextHistoicFlow.setTaskId(newTaskId);
				nextHistoicFlow.setPreTaskId(startTask.getId());
				nextHistoicFlow.setLinkKey(newTask.getTaskDefinitionKey());
				nextHistoicFlow.setLinkName(newTask.getName());
				nextHistoicFlow.setAssigneeId(newUserId);
				nextHistoicFlow.setAssigneeName(staffBaseInfo.getStaffName(newUserId));
				nextHistoicFlow.setOrgId(staffBaseInfo.getStaffOrgId(newUserId));
				nextHistoicFlow.setOrgName(staffBaseInfo.getStaffOrgName(newUserId));
				nextHistoicFlow.setStartTime(newTask.getCreateTime());
				nextHistoicFlow.setWithdraw(false);

				flowHistoryDao.insertNewFlowHistory(nextHistoicFlow);
			}
		}
		log.debug("the second task flow has been done .");

		return processInstanceId;
	}

	/**
	 * 流程发起方法重载方法，可指定目标环节任务Key
	 * 
	 * @param ii
	 *            参数提供实体
	 * @param vars
	 *            用户流程发起时需要的额外参数
	 * @param comment
	 *            首环节发起意见
	 * @param withdraw
	 *            推进后是否可撤回
	 * @param taskDefinitionKey
	 *            目标环节定义key
	 * @return 流程实例ID
	 */
	@Override
	public String startProcess(WorkflowEntityForStart ii, Map<String, Object> vars, String comment, boolean withdraw,
			String taskDefinitionKey) {

		/*
		 * 取出流程发起时的必要信息
		 * 
		 * 流程标题、流程编码、业务主键及发起人相关信息
		 */
		// 用户ID，多岗系统为当前岗位ID
		String userId = ii.getUserId();
		String userName = staffBaseInfo.getStaffName(userId);
		String orgId = staffBaseInfo.getStaffOrgId(userId);
		String orgName = staffBaseInfo.getStaffOrgName(userId);

		String title = ii.getTitle();
		String processDefinitionKey = ii.getProcessDefinitionKey();
		String businessKey = ii.getBusinessKey();

		// 暂存次环节处理人并设置手环节处理人为发起人
		String assignee = String.valueOf(vars.get("assignee"));
		vars.put("assignee", userId);

		// 存入流程标题
		vars.put("title", title);
		// 存储发起人名称
		vars.put("applyUserName", userName);

		identityService.setAuthenticatedUserId(userId);
		ProcessInstance pi = null;
		if (StringUtils.isBlank(businessKey)) {
			pi = runtimeService.startProcessInstanceByKey(processDefinitionKey, vars);
		} else {
			pi = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, vars);
		}
		identityService.setAuthenticatedUserId(null);
		String processInstanceId = pi.getId();
		log.debug("process has started , the processInstanceId is :" + processInstanceId);

		Task startTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
		HistoicFlowVO firstHistoicFlow = new HistoicFlowVO();
		firstHistoicFlow.setProcessInstanceId(processInstanceId);
		firstHistoicFlow.setTaskId(startTask.getId());
		firstHistoicFlow.setPreTaskId("");
		firstHistoicFlow.setLinkKey(startTask.getTaskDefinitionKey());
		firstHistoicFlow.setLinkName(startTask.getName());
		firstHistoicFlow.setAssigneeId(userId);
		firstHistoicFlow.setAssigneeName(userName);
		firstHistoicFlow.setOrgId(orgId);
		firstHistoicFlow.setOrgName(orgName);
		firstHistoicFlow.setHandleType(WorkflowConstants.HANDLE_TYPE_PASS);
		firstHistoicFlow.setStartTime(startTask.getCreateTime());
		this.addComment(comment, startTask.getId(), pi.getId(), true);
		firstHistoicFlow.setUserComment(comment);

		// 当前任务中获取的执行流ID
		String executionId = taskService.createTaskQuery().taskId(startTask.getId()).singleResult().getExecutionId();
		// 受理变更为用户传入值
		vars.put("assignee", assignee);
		Command<Object> cmd = new JumpToActivityCmd(executionId, taskDefinitionKey, vars);
		managementService.executeCommand(cmd);
		firstHistoicFlow.setEndTime(new Date());
		firstHistoicFlow.setWithdraw(withdraw);
		firstHistoicFlow.setQuickBack(false);

		flowHistoryDao.insertWholeFlowHistory(firstHistoicFlow);
		log.debug("the first task has been done .");

		/**
		 * 完成流程推动后，如果存在新产生的用户任务，需要添加新增的历史记录，暂默认流程推进前的瞬间之后生成的任务均为待处理的用户任务
		 * 20170511修改，为兼容老版本数据库时间精度丢失问题
		 */
		List<Task> newTaskList = findNewTaskByInstanceId(processInstanceId);

		if (newTaskList != null && newTaskList.size() > 0) {

			for (int i = 0; i < newTaskList.size(); i++) {

				Task newTask = newTaskList.get(i);
				String newUserId = newTask.getAssignee();
				String newTaskId = newTask.getId();

				HistoicFlowVO nextHistoicFlow = new HistoicFlowVO();
				nextHistoicFlow.setProcessInstanceId(processInstanceId);
				nextHistoicFlow.setTaskId(newTaskId);
				nextHistoicFlow.setPreTaskId(startTask.getId());
				nextHistoicFlow.setLinkKey(newTask.getTaskDefinitionKey());
				nextHistoicFlow.setLinkName(newTask.getName());
				nextHistoicFlow.setAssigneeId(newUserId);
				nextHistoicFlow.setAssigneeName(staffBaseInfo.getStaffName(newUserId));
				nextHistoicFlow.setOrgId(staffBaseInfo.getStaffOrgId(newUserId));
				nextHistoicFlow.setOrgName(staffBaseInfo.getStaffOrgName(newUserId));
				nextHistoicFlow.setStartTime(newTask.getCreateTime());
				nextHistoicFlow.setWithdraw(false);

				flowHistoryDao.insertNewFlowHistory(nextHistoicFlow);
			}
		}
		log.debug("the second task flow has been done .");

		return processInstanceId;
	}

	/**
	 * 流程推动方法重载，完全依照流程定义流转流程
	 * 
	 * @param taskId
	 *            当前任务的ID
	 * @param processInstanceId
	 *            当前任务所在实例的ID
	 * @param vars
	 *            传入推动流程所需参数
	 * @param comment
	 *            环节处理意见
	 * @param withdraw
	 *            推进后是否可撤回
	 * @return 新产生任务ID数组
	 */
	@Override
	public String[] pushProcess(String taskId, String processInstanceId, Map<String, Object> vars, String comment,
			boolean withdraw) {
		// 流程加入处理意见
		this.addComment(comment, taskId, processInstanceId, false);
		HistoicFlowVO currentHistoicFlow = new HistoicFlowVO();
		// 处理方式为必填项
		currentHistoicFlow.setTaskId(taskId);
		currentHistoicFlow.setHandleType(String.valueOf(vars.get("handleType")));
		currentHistoicFlow.setUserComment(comment);

		// 将处理类型设本地变量并在全局变量中删除
		// taskService.setVariableLocal(taskId, "handleType",
		// String.valueOf(vars.get("handleType")));
		vars.remove("handleType");
		taskService.complete(taskId, vars);
		currentHistoicFlow.setEndTime(new Date());
		currentHistoicFlow.setWithdraw(withdraw);
		currentHistoicFlow.setQuickBack(false);

		flowHistoryDao.supplementaryFlowHistory(currentHistoicFlow);
		log.debug("pass the process .");

		/**
		 * 完成流程推动后，如果存在新产生的用户任务，需要添加新增的历史记录，暂默认流程推进前的瞬间之后生成的任务均为待处理的用户任务
		 * 20170511修改，为兼容老版本数据库时间精度丢失问题
		 */
		List<Task> newTaskList = findNewTaskByInstanceId(processInstanceId);
		String[] newTaskIds = new String[0];

		if (newTaskList != null && newTaskList.size() > 0) {

			newTaskIds = new String[newTaskList.size()];

			for (int i = 0; i < newTaskList.size(); i++) {

				Task newTask = newTaskList.get(i);
				String newUserId = newTask.getAssignee();
				String newTaskId = newTask.getId();
				newTaskIds[i] = newTaskId;

				HistoicFlowVO nextHistoicFlow = new HistoicFlowVO();
				nextHistoicFlow.setProcessInstanceId(processInstanceId);
				nextHistoicFlow.setTaskId(newTaskId);
				nextHistoicFlow.setPreTaskId(taskId);
				nextHistoicFlow.setLinkKey(newTask.getTaskDefinitionKey());
				nextHistoicFlow.setLinkName(newTask.getName());
				nextHistoicFlow.setAssigneeId(newUserId);
				nextHistoicFlow.setAssigneeName(staffBaseInfo.getStaffName(newUserId));
				nextHistoicFlow.setOrgId(staffBaseInfo.getStaffOrgId(newUserId));
				nextHistoicFlow.setOrgName(staffBaseInfo.getStaffOrgName(newUserId));
				nextHistoicFlow.setStartTime(newTask.getCreateTime());
				nextHistoicFlow.setWithdraw(false);

				flowHistoryDao.insertNewFlowHistory(nextHistoicFlow);
			}
		}

		log.debug("if the instance isn't finished,the next task flow has been done .");

		return newTaskIds;
	}

	/**
	 * 流程推动方法重载，可定义推进至预到达的环节
	 * 
	 * @param taskId
	 *            当前任务ID
	 * @param processInstanceId
	 *            当前任务所在实例的ID
	 * @param vars
	 *            传入推动流程所需参数
	 * @param comment
	 *            环节处理意见
	 * @param withdraw
	 *            推进后是否可撤回
	 * @param taskDefinitionKey
	 *            预跳转到环节在XML文件中的元素定义ID
	 * @return 新产生任务ID数组
	 */
	@Override
	public String[] pushProcess(String taskId, String processInstanceId, Map<String, Object> vars, String comment,
			boolean withdraw, String taskDefinitionKey) {

		return this.pushProcess(taskId, processInstanceId, vars, comment, withdraw, taskDefinitionKey, false);
	}

	/**
	 * 流程推动方法重载，可定义推进至预到达的环节
	 * 
	 * @param taskId
	 *            当前任务ID
	 * @param processInstanceId
	 *            当前任务所在实例的ID
	 * @param vars
	 *            传入推动流程所需参数
	 * @param comment
	 *            环节处理意见
	 * @param withdraw
	 *            推进后是否可撤回
	 * @param taskDefinitionKey
	 *            预跳转到环节在XML文件中的元素定义ID
	 * @param quickBack
	 *            当前推进是否为简退-退回操作
	 * @return 新产生任务ID数组
	 */
	private String[] pushProcess(String taskId, String processInstanceId, Map<String, Object> vars, String comment,
			boolean withdraw, String taskDefinitionKey, boolean quickBack) {
		// 流程加入处理意见
		this.addComment(comment, taskId, processInstanceId, false);
		HistoicFlowVO currentHistoicFlow = new HistoicFlowVO();
		// 处理方式为必填项
		currentHistoicFlow.setTaskId(taskId);
		currentHistoicFlow.setHandleType(String.valueOf(vars.get("handleType")));
		currentHistoicFlow.setUserComment(comment);

		// 当前任务中获取的执行流ID
		String executionId = taskService.createTaskQuery().taskId(taskId).singleResult().getExecutionId();
		Command<Object> cmd = new JumpToActivityCmd(executionId, taskDefinitionKey, vars);
		managementService.executeCommand(cmd);
		currentHistoicFlow.setEndTime(new Date());
		currentHistoicFlow.setWithdraw(withdraw);
		currentHistoicFlow.setQuickBack(quickBack);

		flowHistoryDao.supplementaryFlowHistory(currentHistoicFlow);
		log.debug("pass the process .");

		/**
		 * 完成流程推动后，如果存在新产生的用户任务，需要添加新增的历史记录，暂默认流程推进前的瞬间之后生成的任务均为待处理的用户任务
		 * 20170511修改，为兼容老版本数据库时间精度丢失问题
		 */
		List<Task> newTaskList = findNewTaskByInstanceId(processInstanceId);

		String[] newTaskIds = new String[0];

		if (newTaskList != null && newTaskList.size() > 0) {

			newTaskIds = new String[newTaskList.size()];

			for (int i = 0; i < newTaskList.size(); i++) {

				Task newTask = newTaskList.get(i);
				String newUserId = newTask.getAssignee();
				String newTaskId = newTask.getId();
				newTaskIds[i] = newTaskId;

				HistoicFlowVO nextHistoicFlow = new HistoicFlowVO();
				nextHistoicFlow.setProcessInstanceId(processInstanceId);
				nextHistoicFlow.setTaskId(newTaskId);
				nextHistoicFlow.setPreTaskId(taskId);
				nextHistoicFlow.setLinkKey(newTask.getTaskDefinitionKey());
				nextHistoicFlow.setLinkName(newTask.getName());
				nextHistoicFlow.setAssigneeId(newUserId);
				nextHistoicFlow.setAssigneeName(staffBaseInfo.getStaffName(newUserId));
				nextHistoicFlow.setOrgId(staffBaseInfo.getStaffOrgId(newUserId));
				nextHistoicFlow.setOrgName(staffBaseInfo.getStaffOrgName(newUserId));
				nextHistoicFlow.setStartTime(newTask.getCreateTime());
				nextHistoicFlow.setWithdraw(false);

				flowHistoryDao.insertNewFlowHistory(nextHistoicFlow);
			}
		}

		log.debug("if the instance isn't finished,the next task flow has been done .");

		return newTaskIds;
	}

	/**
	 * 自由流修改定义方法，无法解决并发问题
	 * 
	 * @param insId
	 *            流程实例ID
	 * @param currentTaskId
	 *            当前任务ID
	 * @param targetTaskKey
	 *            目标环节定义key
	 * @param variables
	 *            流程变量
	 */
	@Deprecated
	public void gotoAnyActiviti(String insId, String currentTaskId, String targetTaskKey,
			Map<String, Object> variables) {

		// 获取当前任务流程定义中Key，即XML文件中id
		Task task = taskService.createTaskQuery().taskId(currentTaskId).singleResult();
		String currentTaskKey = task.getTaskDefinitionKey();

		// 取流程定义实体
		ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
				.getDeployedProcessDefinition(task.getProcessDefinitionId());

		synchronized (processDefinitionEntity) {
			// 当前任务定义实体
			ActivityImpl currentTask = processDefinitionEntity.findActivity(currentTaskKey);
			// 目标任务定义实体
			ActivityImpl targetTask = processDefinitionEntity.findActivity(targetTaskKey);

			/*
			 * 将定义中的输出顺序流暂存后切段
			 */
			List<PvmTransition> oldPvmTransition = currentTask.getOutgoingTransitions();
			List<PvmTransition> tempPvmTransition = new ArrayList<PvmTransition>();

			tempPvmTransition.addAll(oldPvmTransition);
			// 清除原先的路径
			oldPvmTransition.clear();
			// 创建新路径
			TransitionImpl newTran = currentTask.createOutgoingTransition();
			newTran.setDestination(targetTask);

			// 添加批注
			// taskService.addComment(task.getId(), insId, "意见");
			// variables.put("deptLeaderPass", true);
			// 完成任务
			taskService.complete(task.getId(), variables);

			/*
			 * 恢复路径(需要清除目标节点的新流入项 )
			 */
			targetTask.getIncomingTransitions().remove(newTran);
			currentTask.getOutgoingTransitions().clear();
			oldPvmTransition.addAll(tempPvmTransition);
		}
	}

	/**
	 * 流程删除功能，直接将流程终止； 有关业务数据的回滚，业务模块自行处理
	 * 
	 * @param processInstanceId
	 *            流程实例ID
	 * @param taskId
	 *            任务ID
	 * @param deleteReason
	 *            删除原因
	 */
	@Override
	public void deleteProcessIntanceButHistoric(String processInstanceId, String taskId, String deleteReason) {

		if (StringUtils.isBlank(deleteReason)) {
			deleteReason = "deleteProcessIntance...";
		}
		// 获取当前任务，查询不到即实际已完成或不存在
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		if (task == null) {
			return;
		}

		HistoicFlowVO currentHistoicFlow = new HistoicFlowVO();
		// 处理方式为必填项
		currentHistoicFlow.setTaskId(taskId);
		currentHistoicFlow.setHandleType(WorkflowConstants.HANDLE_TYPE_BREAK);
		currentHistoicFlow.setUserComment(deleteReason);

		this.addComment(deleteReason, taskId, processInstanceId, false);
		// taskService.setVariableLocal(taskId, "handleType",
		// WorkflowConstants.HANDLE_TYPE_BREAK);
		runtimeService.deleteProcessInstance(processInstanceId, WorkflowConstants.COMPLETED_TYPE_BREAK);
		currentHistoicFlow.setEndTime(new Date());
		currentHistoicFlow.setWithdraw(false);
		currentHistoicFlow.setQuickBack(false);

		flowHistoryDao.supplementaryFlowHistory(currentHistoicFlow);
		log.debug("break the process .");
	}

	/**
	 * 任务撤回实现
	 * 
	 * @param taskId
	 *            当前已办任务ID
	 * @param processInstanceId
	 *            实例ID
	 * @return 是否成功
	 */
	@Override
	public boolean withdrawProcess(String taskId, String processInstanceId) {

		// 需再判断一次是否仍可撤回
		HistoicFlowVO hfo = flowHistoryDao.findFlowHistory(processInstanceId, taskId);
		// 获取等待任务的ID
		Task currentTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();

		if (hfo != null && currentTask != null && hfo.isWithdraw()) {

			// 首先需要将当前任务的前一条提交记录标记为不可撤回
			flowHistoryDao.signTask(processInstanceId, taskId);
			// 获取目标环节定义
			String taskDefinitionKey = historyService.createHistoricTaskInstanceQuery().taskId(taskId).finished()
					.processInstanceId(processInstanceId).singleResult().getTaskDefinitionKey();

			String currentTaskId = currentTask.getId();
			// 评论暂置为空字符串
			String comment = "";
			Map<String, Object> vars = new HashMap<String, Object>();
			vars.put("handleType", WorkflowConstants.HANDLE_TYPE_WITHDRAW);
			// 获取撤回处理人ID
			String userId = hfo.getAssigneeId();
			vars.put("assignee", userId);

			// 任务处理之前改变原发出任务的处理人，否则任务完毕后报错找不到任务
			taskService.setAssignee(currentTaskId, userId);
			this.pushProcess(currentTaskId, processInstanceId, vars, comment, false, taskDefinitionKey);

			// 更新原等待任务受理人及部门信息
			HistoicFlowVO preHfo = new HistoicFlowVO();
			preHfo.setAssigneeId(userId);
			preHfo.setAssigneeName(staffBaseInfo.getStaffName(userId));
			preHfo.setOrgId(staffBaseInfo.getStaffOrgId(userId));
			preHfo.setOrgName(staffBaseInfo.getStaffOrgName(userId));
			preHfo.setProcessInstanceId(processInstanceId);
			preHfo.setTaskId(currentTaskId);
			flowHistoryDao.updateAssigneeForWithdraw(preHfo);

			return true;
		} else {
			return false;
		}
	}

	/**
	 * 简退-退回操作
	 * 
	 * @param processInstanceId
	 *            流程实例ID
	 * @param currentTaskId
	 *            当前进行简退回退操作的任务ID
	 * @param comment
	 *            退回命令
	 * @return 是否回退成功
	 */
	@Override
	public boolean backForQuick(String processInstanceId, String currentTaskId, String comment) {

		// 通过拓展的流转历史信息表获取首环节定义编码及处理人
		List<HistoicFlowVO> list = flowHistoryDao.findFlowHistory(processInstanceId);

		if (list == null || list.size() == 0) {
			return false;
		}

		String taskDefinitionKey = list.get(0).getLinkKey();
		String userId = list.get(0).getAssigneeId();
		Map<String, Object> vars = new HashMap<String, Object>();
		vars.put("handleType", WorkflowConstants.HANDLE_TYPE_BACK);
		vars.put("assignee", userId);

		this.pushProcess(currentTaskId, processInstanceId, vars, comment, true, taskDefinitionKey, true);

		return true;
	}

	/**
	 * 简退-推进操作
	 * 
	 * @param processInstanceId
	 *            流程实例ID
	 * @param currentTaskId
	 *            当前进行简退推进操作的任务ID
	 * @param comment
	 *            推进意见
	 * @return 是否推进成功
	 */
	@Override
	public boolean pushForQuick(String processInstanceId, String currentTaskId, String comment) {

		// 获取到目标环节信息:环节定义KEY,环节处理人ID
		HistoicFlowVO target = findQuickStartHistoic(processInstanceId, currentTaskId);
		if (target == null) {
			return false;
		}
		String taskDefinitionKey = target.getLinkKey();
		String userId = target.getAssigneeId();

		Map<String, Object> vars = new HashMap<String, Object>();
		vars.put("handleType", WorkflowConstants.HANDLE_TYPE_PASS);
		vars.put("assignee", userId);

		// 简退-推进操作，默认可撤回
		this.pushProcess(currentTaskId, processInstanceId, vars, comment, true, taskDefinitionKey, false);

		return true;
	}

	/**
	 * 转派功能，当前任务更换处理人，记录流转历史
	 * 
	 * @param taskId
	 *            当前任务ID
	 * @param processInstanceId
	 *            流程实例ID
	 * @param assignee
	 *            预指定处理人
	 * @return 新任务ID
	 */
	@Override
	public String turnAssignee(String taskId, String processInstanceId, String assignee, String comment) {
		// 获取当前任务环节定义作为目标环节
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		String taskDefinitionKey = task.getTaskDefinitionKey();

		Map<String, Object> vars = new HashMap<String, Object>();
		vars.put("assignee", assignee);
		vars.put("handleType", WorkflowConstants.HANDLE_TYPE_TURN);

		String[] taskIds = pushProcess(taskId, processInstanceId, vars, comment, true, taskDefinitionKey);

		return taskIds[0];
	}

	/**
	 * 公用评论添加方法
	 * 
	 * @param comment
	 *            评论内容
	 * @param taskId
	 *            任务ID
	 * @param procInsId
	 *            流程实例ID
	 * @param startFlag
	 *            是否为流程发起
	 */
	private void addComment(String comment, String taskId, String procInsId, boolean startFlag) {

		if (StringUtils.isBlank(comment)) {
			if (startFlag) {
				// 根据需求可变更
				comment = "首环节发起";
			} else {
				comment = "";
			}
		}

		log.debug("add the task comment done.");
		taskService.addComment(taskId, procInsId, comment);
	}

	/**
	 * 流程实例的业务流转历史
	 * 
	 * @param processInstanceId
	 *            流程实例ID
	 * @return 流转历史集合
	 */
	@Override
	public List<HistoicFlowVO> histoicFlowList(String processInstanceId) {

		List<HistoicFlowVO> list = flowHistoryDao.findFlowHistory(processInstanceId);
		if (list == null) {
			return new ArrayList<HistoicFlowVO>();
		}

		return list;
	}

	/**
	 * 流程实例的业务流转历史
	 * 
	 * @param procInsId
	 *            流程实例ID
	 * @return 流转历史集合
	 */
	@Deprecated
	public List<HistoicFlowVO> userTaskHistoicFlowList(String procInsId) {
		// 定义最终返回的结果集
		List<HistoicFlowVO> results = new LinkedList<HistoicFlowVO>();

		// 获取全部经过用户任务节点-20161103调整为原生查询，解决前两环节记录乱序问题
		/*
		 * List<HistoricActivityInstance> list = this.getHistoryService()
		 * .createHistoricActivityInstanceQuery()
		 * .processInstanceId(procInsId).activityType("userTask")
		 * .orderByHistoricActivityInstanceStartTime().asc()
		 * .orderByHistoricActivityInstanceEndTime().asc().list();
		 */
		String tableName = managementService.getTableName(HistoricActivityInstance.class);
		StringBuilder sb = new StringBuilder("SELECT RES.*");
		sb.append(" FROM ").append(tableName).append(" RES").append(" WHERE RES.PROC_INST_ID_ = #{procInsId}")
				.append(" AND RES.ACT_TYPE_ = 'userTask'").append(" ORDER BY RES.START_TIME_ ASC,")
				.append(" CASE WHEN RES.END_TIME_ IS NULL THEN 2 ELSE 1 END ASC,").append(" RES.END_TIME_ ASC");
		List<HistoricActivityInstance> list = historyService.createNativeHistoricActivityInstanceQuery()
				.sql(sb.toString()).parameter("procInsId", procInsId).list();

		for (int i = 0; i < list.size(); i++) {
			// 已经过节点历史记录
			HistoricActivityInstance histIns = list.get(i);

			if (histIns != null && StringUtils.isNotBlank(histIns.getAssignee())) {
				// 创建HistoicFlowVO实体并存入有效内容
				HistoicFlowVO hfo = new HistoicFlowVO();
				// 任务处理人ID，相应业务模块需要查询其具体信息
				hfo.setAssigneeId(histIns.getAssignee());
				// 环节名称
				hfo.setLinkName(histIns.getActivityName());
				// 设置任务任务到达时间及完成时间
				hfo.setStartTime(histIns.getStartTime());
				hfo.setEndTime(histIns.getEndTime());
				// 可获取到任务ID即设置任务评论
				if (StringUtils.isNotBlank(histIns.getTaskId())) {
					List<Comment> commentList = taskService.getTaskComments(histIns.getTaskId());
					// 正常情况下仅有一条
					if (commentList.size() > 0) {
						hfo.setUserComment(commentList.get(0).getFullMessage());
					} else {
						hfo.setUserComment("");
					}
				}

				results.add(hfo);
			}
		}

		return results;
	}

	/**
	 * 获取流程图 当前任务停留环节高亮
	 * 
	 * @param processInstanceId
	 *            流程实例ID
	 * @return 图像输入流
	 */
	@Override
	public InputStream getDiagramInputStream(String processInstanceId) {

		// 实例在活动中，获取实例
		ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId)
				.singleResult();
		// 活动已完结，获取实例
		HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery()
				.processInstanceId(processInstanceId).singleResult();
		if (pi == null && hpi == null) {
			return null;
		}

		// 根据获取到的流程实例获取流程是定义实体
		ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) repositoryService
				.getProcessDefinition(pi == null ? hpi.getProcessDefinitionId() : pi.getProcessDefinitionId());

		// 准备参数，分为以下部分
		// 1.业务流程模型
		BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
		// 2.需高亮节点
		List<String> activeActivityIds = new LinkedList<String>();
		// 判断流程实例仍未完结，采用运行组件的查询结果，若流程结束采用历史组件查询结果
		if (pi != null) {
			activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
		} else {
			/*
			 * 此处深入数据时发现，使用自由流命令模式的问题已经体现： 1.使用自由流跳转时，不会更新本次完成活动历史表相应记录的完成时间
			 * 2.原生任务完成方法会更新，但更新的是时间正序排，开始时间最早那条符合环节定义的记录时间 结论：
			 * 1.自由流与非自由流混合使用，活动历史表结束时间有问题却暂时不可避免，应尽量避免使用活动历史表数据
			 * 2.若需取时间差等信息要从任务历史表中取
			 * 3.由于现行模式流程均为用户任务，故此法可靠，若引入与其他类型任务，整个封装思路需要重新考虑
			 *
			 * 上述问题于20170227，自由流改用executionEntity.destroyScope()方式解决
			 */
			// 最终环节名称
			String endLinkName = "";
			// 对于以“流程作废”结束的流程，将结束节点固定至结束节点，正常结束流程固定至结束事件
			if (!WorkflowConstants.COMPLETED_TYPE_BREAK.equals(hpi.getDeleteReason())) {
				endLinkName = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)
						.activityType("endEvent").singleResult().getActivityId();
			} else {
				endLinkName = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId)
						.finished().orderByHistoricTaskInstanceEndTime().desc().list().get(0).getTaskDefinitionKey();
			}

			// 20160923 考虑流程终止的情况，改变结束节点的出具策略，要求结束事件命名必须为“endevent1”
			// 20170222 根据流程结束方式变更处理策略，分为正常结束与中止两种情况，不再要求结束事件命名
			activeActivityIds.add(endLinkName);
		}

		log.debug("prepared the param for diagram done :" + activeActivityIds);
		// 获取流程引擎的配置
		ProcessEngineConfigurationImpl processEngineConfiguration = processEngine.getProcessEngineConfiguration();
		// Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl)
		// processEngineConfiguration);
		// 引擎配置中获取流程图绘制器
		ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
		// 调用绘制器绘图方法绘制当前等待任务高亮的流程图
		InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", activeActivityIds,
				Collections.<String>emptyList(), processEngineConfiguration.getActivityFontName(),
				processEngineConfiguration.getLabelFontName(), null, null, 1.0);

		log.debug("return the imageStream successfully .");

		return imageStream;
	}

	/**
	 * 浏览待办后，为上环节处理人不可撤回打标
	 * 
	 * @param processInstanceId
	 *            实例ID
	 * @param taskId
	 *            任务ID
	 * @return 更新记录数，正常为1
	 */
	@Override
	public int signTask(String processInstanceId, String taskId) {

		HistoicFlowVO currentHfo = flowHistoryDao.findFlowHistory(processInstanceId, taskId);
		String preTaskId = currentHfo.getPreTaskId();

		return flowHistoryDao.signTask(processInstanceId, preTaskId);
	}

	/**
	 * 判断当前已办环节是否可以撤回
	 * 
	 * @param processInstanceId
	 *            实例
	 * @param taskId
	 *            任务
	 * @return 是否可回撤
	 */
	@Override
	public boolean isCanWithDraw(String processInstanceId, String taskId) {

		HistoicFlowVO currentHfo = flowHistoryDao.findFlowHistory(processInstanceId, taskId);
		return currentHfo.isWithdraw();
	}

	/**
	 * 是否为简退后的提交首环节
	 * 
	 * @param processInstanceId
	 *            流程实例ID
	 * @param taskId
	 *            任务ID
	 * @return 是否可直接提交至退回环节的退回人
	 */
	@Override
	public boolean isBackForQuick(String processInstanceId, String taskId) {

		boolean quickBack = false;

		HistoicFlowVO startHfo = flowHistoryDao.findFlowHistory(processInstanceId).get(0);
		HistoicFlowVO currentHfo = flowHistoryDao.findFlowHistory(processInstanceId, taskId);
		HistoicFlowVO hfo = findQuickStartHistoic(processInstanceId, taskId);
		if (hfo != null && hfo.isQuickBack() && currentHfo.getLinkKey().equals(startHfo.getLinkKey())) {
			quickBack = true;
		}

		return quickBack;
	}

	/**
	 * 根据简退-推进环节信息查找对应简退-退回环节记录信息
	 * 
	 * @param processInstanceId
	 *            流程实例ID
	 * @param taskId
	 *            任务ID
	 * @return 对应的环节记录
	 */
	private HistoicFlowVO findQuickStartHistoic(String processInstanceId, String taskId) {

		List<HistoicFlowVO> list = flowHistoryDao.findFlowHistory(processInstanceId);
		HistoicFlowVO currentHfv = null;
		for (int i = list.size() - 1; i >= 0; i--) {
			HistoicFlowVO hfo = list.get(i);
			list.remove(i);
			String hfoTaskId = hfo.getTaskId();
			if (taskId.equals(hfoTaskId)) {
				currentHfv = hfo;
				break;
			}
		}

		HistoicFlowVO result = null;
		String tmpTaskId = currentHfv.getPreTaskId();
		for (int i = list.size() - 1; i >= 0; i--) {
			HistoicFlowVO hfo = list.get(i);
			// 依照上环节任务ID查找对应上环节操作记录
			if (tmpTaskId.equals(hfo.getTaskId())) {
				tmpTaskId = hfo.getPreTaskId();
				if (WorkflowConstants.HANDLE_TYPE_BACK.equals(hfo.getHandleType())) {
					result = hfo;
					break;
				}
			}
		}

		return result;
	}

	/**
	 * 通过流程实例ID获取还未写入历史的全部任务，仅用于需获取新产生任务用于写流转历史时使用
	 * 
	 * @param processInstanceId
	 *            流程实例ID
	 * @return 全部任务实体
	 */
	private List<Task> findNewTaskByInstanceId(String processInstanceId) {
		// 获取运行时任务表表名
		String taskName = managementService.getTableName(Task.class);
		// 组装查询语句
		StringBuffer sb = new StringBuffer("SELECT RES.* FROM ");
		sb.append(taskName).append(" RES WHERE RES.PROC_INST_ID_ = #{processInstanceId} AND RES.ID_ NOT IN")
				.append(" (SELECT TASK_ID FROM FLOW_HISTORY WHERE PROC_INST_ID = #{processInstanceId})");

		return taskService.createNativeTaskQuery().sql(sb.toString()).parameter("processInstanceId", processInstanceId)
				.list();
	}

	/**
	 * 查看任务是否已经完成
	 * 
	 * @param taskId
	 *            任务ID
	 * @return 是否已经完成
	 */
	@Override
	public boolean isFinishedTask(String taskId) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		if (task == null) {
			return true;
		}
		return false;
	}

	/**
	 * 获取流程图 此流程图无节点高亮，仅供已完结的流程使用
	 * 
	 * @param processInstanceId
	 *            流程实例ID
	 * @return 图像输入流
	 */
	@Deprecated
	public InputStream getDiagramInputStreamForHistory(String processInstanceId) {

		ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId)
				.singleResult();

		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(pi.getProcessDefinitionId()).singleResult();

		String diagramResourceName = processDefinition.getDiagramResourceName();
		InputStream imageStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
				diagramResourceName);

		return imageStream;
	}

	@Autowired
	public void setProcessEngine(@Qualifier("processEngine") ProcessEngineFactoryBean processEngine) {
		this.processEngine = processEngine;
	}

	@Autowired
	public void setRepositoryService(RepositoryService repositoryService) {
		this.repositoryService = repositoryService;
	}

	@Autowired
	public void setRuntimeService(RuntimeService runtimeService) {
		this.runtimeService = runtimeService;
	}

	@Autowired
	public void setTaskService(TaskService taskService) {
		this.taskService = taskService;
	}

	@Autowired
	public void setHistoryService(HistoryService historyService) {
		this.historyService = historyService;
	}

	@Autowired
	public void setManagementService(ManagementService managementService) {
		this.managementService = managementService;
	}

	@Autowired
	public void setIdentityService(IdentityService identityService) {
		this.identityService = identityService;
	}

	@Autowired
	public void setFlowHistoryDao(FlowHistoryDao flowHistoryDao) {
		this.flowHistoryDao = flowHistoryDao;
	}

	@Autowired
	public void setStaffBaseInfo(StaffBaseInfo staffBaseInfo) {
		this.staffBaseInfo = staffBaseInfo;
	}

}
